package solverStrategies;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.junit.Assert;

import sudoku.SudokuEinzelfeld;
import sudoku.SudokuSpielfeld;

/**
 * Hidden Single: Einzig moeglicher Ort an dem eine Zahl stehen kann.
 * Algorithmus: Gehe jede Zeile, Reihe und jeden Block durch und prüfe für jede
 * Zahl, wieviele Platzierungsmoeglichkeiten es gibt. Gibt es nur 1 Moeglichkeit
 * ist das die Loesung. Aus allen anderen Konstrukzen muessen die Zahlen
 * gestrichen werden.
 * 
 * Arbeitet auf den "possibleNumber"-Listen der einzelnen Felder. Braucht eine
 * Vorverarbeitung um sinnvolle Ergebnisse zu liefern.
 * 
 */
public class HiddenSingle implements SolverAlgorithm {

	private final int LEVEL = 1;

	@Override
	public boolean manipulateSudoku(SudokuSpielfeld sf) {
		/* Look at every number */
		for (int i = 0; i < 9; i++) {
			List<SudokuEinzelfeld> row = sf.returnRow(i);
			List<SudokuEinzelfeld> col = sf.returnCol(i);
			List<SudokuEinzelfeld> block = sf.returnblock(i + 1);
			identifyAndSetNumbers(row, sf);
			identifyAndSetNumbers(col, sf);
			identifyAndSetNumbers(block, sf);

		}
		return false;
	}

	private int[] countNumbers(List<SudokuEinzelfeld> construct) {
		int[] counter = new int[construct.size()]; // index = number-1

		for (SudokuEinzelfeld sef : construct) {
			Set<Integer> possNumbers = sef.getPossibleNumbers();
			for (Integer integer : possNumbers) {
				int tmp = counter[integer - 1];
				tmp++;
				counter[integer - 1] = tmp;
			}
		}

		return counter;
	}

	private void setIdentifiedNumbers(List<Integer> numbers,
			List<SudokuEinzelfeld> construct, SudokuSpielfeld sf) {
		for (Integer integer : numbers) {
			for (SudokuEinzelfeld sudokuEinzelfeld : construct) {
				Set<Integer> possNumbers = sudokuEinzelfeld
						.getPossibleNumbers();
				if (possNumbers.contains(integer)) {
					sf.setNumberWithID(sudokuEinzelfeld.getId(), integer,
							"HiddenSingle");
					break;
				}
			}
		}
	}

	private List<Integer> identifieNumbers(int[] counter) {
		List<Integer> numbers = new LinkedList<Integer>();
		for (int i = 0; i < counter.length; i++) {
			if (counter[i] == 1) {
				numbers.add(i + 1);
			}
		}
		return numbers;
	}

	@Override
	public int returnLevel() {
		return LEVEL;
	}

	private void identifyAndSetNumbers(List<SudokuEinzelfeld> construct,
			SudokuSpielfeld sf) {
		int[] counter = countNumbers(construct);
		List<Integer> numbersList = identifieNumbers(counter);

		setIdentifiedNumbers(numbersList, construct, sf);
	}

	public static void main(String[] args) {
		List<SolverAlgorithm> solverList = new ArrayList<SolverAlgorithm>();
		solverList.add(new BasicReduce());
		solverList.add(new NakedSingle());
		solverList.add(new HiddenSingle());
		solverList.add(new AdvancedReduce());
		SudokuSpielfeld sf = new SudokuSpielfeld();
		System.out.println("START");
		Assert.assertTrue(sf.isValid(sf.getSudokuFeld()));
		boolean changes = false;
		int round = 1;
		while (!sf.isReady(sf.getSudokuFeld())) {
			boolean changesTmp = false;
			changes = false;
			for (SolverAlgorithm solverAlgorithm : solverList) {
				System.out.println(solverAlgorithm.getClass().getName());
				changesTmp = solverAlgorithm.manipulateSudoku(sf);
				while (changesTmp) {
					if (changesTmp) {
						changes = changesTmp;
					}
					System.out.println(solverAlgorithm);
					System.out.println("Changes: " + changes);
					if (changes) {
						sf.printSudoku();
						sf.printPossibleNumbers();
					}
					Assert.assertTrue(sf.validateCompleteSudoku());
					changesTmp = solverAlgorithm.manipulateSudoku(sf);
				}
				System.out.println(round + ". Round");
				sf.printSudoku();
				sf.printPossibleNumbers();
				System.out.println(round + ". Round");
				
			}
			if(!changes){
				BruteForce bf = new BruteForce();
				bf.manipulateSudoku(sf);
			}
			round++;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		System.out.println("Sudoku solved");
	}

}
